/*
 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/xlogging.h"
#include "cmsis_os2.h"
#include "fff.h"
#include "gtest/gtest.h"
#include <cstdlib>

DEFINE_FFF_GLOBALS

class TestThreadapiOpeniot : public ::testing::Test {
public:
    THREAD_HANDLE handle = (THREAD_HANDLE)0xDEADBEEF;
    void *arg = {0};
    THREAD_START_FUNC func = (THREAD_START_FUNC)0xDEADBEEF;
    osMessageQueueId_t queue_id = (osMessageQueueId_t)0xDEADBEEF;
    osThreadId_t thread_id = (osThreadId_t)0xDEADBEEF;

    TestThreadapiOpeniot()
    {
        RESET_FAKE(osMessageQueueNew);
        RESET_FAKE(osThreadNew);
        RESET_FAKE(LogError);
        RESET_FAKE(osDelay);
        RESET_FAKE(osThreadNew);
        RESET_FAKE(osMessageQueueGet);
        RESET_FAKE(osMessageQueueDelete);
    }
};

TEST_F(TestThreadapiOpeniot, creating_thread_api_fails_when_no_handle_is_passed)
{
    EXPECT_EQ(THREADAPI_INVALID_ARG, ThreadAPI_Create(nullptr, this->func, this->arg));
}

TEST_F(TestThreadapiOpeniot, creating_thread_api_fails_when_no_callback_is_passed)
{
    EXPECT_EQ(THREADAPI_INVALID_ARG, ThreadAPI_Create(&this->handle, nullptr, this->arg));
}

TEST_F(TestThreadapiOpeniot, creating_thread_api_fails_when_queue_message_cannot_be_created)
{
    osMessageQueueNew_fake.return_val = NULL;

    EXPECT_EQ(THREADAPI_ERROR, ThreadAPI_Create(&this->handle, this->func, this->arg));
}

TEST_F(TestThreadapiOpeniot, creating_thread_api_fails_when_new_thread_cannot_be_created)
{
    osMessageQueueNew_fake.return_val = this->queue_id;
    osThreadNew_fake.return_val = NULL;

    EXPECT_EQ(THREADAPI_ERROR, ThreadAPI_Create(&this->handle, this->func, this->arg));
}

TEST_F(TestThreadapiOpeniot, creating_thread_api_succeeds_when_new_thread_can_be_created)
{
    osMessageQueueNew_fake.return_val = this->queue_id;
    osThreadNew_fake.return_val = this->thread_id;

    EXPECT_EQ(THREADAPI_OK, ThreadAPI_Create(&this->handle, this->func, this->arg));
}

TEST_F(TestThreadapiOpeniot, joining_thread_fails_when_no_handle_is_passed)
{
    int res = 5;

    EXPECT_EQ(THREADAPI_INVALID_ARG, ThreadAPI_Join(nullptr, &res));
}

TEST_F(TestThreadapiOpeniot, joining_thread_fails_when_no_res_is_passed)
{
    EXPECT_EQ(THREADAPI_INVALID_ARG, ThreadAPI_Join(this->handle, nullptr));
}

TEST_F(TestThreadapiOpeniot, joining_thread_fails_when_message_cannot_be_retreived_from_queue)
{
    osMessageQueueNew_fake.return_val = this->queue_id;
    osThreadNew_fake.return_val = this->thread_id;

    EXPECT_EQ(THREADAPI_OK, ThreadAPI_Create(&this->handle, this->func, this->arg));

    int res = 5;
    osMessageQueueGet_fake.return_val = osOK;

    EXPECT_EQ(THREADAPI_OK, ThreadAPI_Join(this->handle, &res));
}

TEST_F(TestThreadapiOpeniot, joining_thread_fails_when_destroying_context_fails)
{
    osMessageQueueNew_fake.return_val = this->queue_id;
    osThreadNew_fake.return_val = this->thread_id;

    EXPECT_EQ(THREADAPI_OK, ThreadAPI_Create(&this->handle, this->func, this->arg));

    int res = 5;
    osMessageQueueGet_fake.return_val = osOK;
    osMessageQueueDelete_fake.return_val = osError;

    EXPECT_EQ(THREADAPI_ERROR, ThreadAPI_Join(this->handle, &res));
}
